package com.zys.http.tool;

import cn.hutool.core.text.CharSequenceUtil;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.ClassLoaderUtil;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.zys.http.entity.HttpConfig;
import com.zys.http.processor.ProcessorContext;
import com.zys.http.processor.basic.PostmanProcessor;
import jdk.jfr.Description;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
import org.jetbrains.kotlin.psi.KtClass;

import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @author zhou ys
 * @since 2023-09-21
 */
@Description("Velocity 模板工具")
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class VelocityTool {

    private static final VelocityEngine ENGINE = new VelocityEngine();
    @Description("环境导出模板")
    private static final String ENV_TEMPLATE_PATH = "template/env.json.vm";
    @Description("接口导出模板")
    private static final String API_TEMPLATE_PATH = "template/api.json.vm";

    @Description("环境导出文件名模板, {}-环境名")
    private static final String EXPORT_ENV_FILE_NAME = "api.tool.export.postman.env.{}.json";
    @Description("接口导出文件名模板, {}-模块名")
    private static final String EXPORT_API_FILE_NAME = "api.tool.export.postman.api.{}.json";

    static {
        Properties p = new Properties();
        p.setProperty("resource.loader", "class");
        p.setProperty("class.resource.loader.class", ClasspathResourceLoader.class.getName());
        p.setProperty(RuntimeConstants.INPUT_ENCODING, StandardCharsets.UTF_8.name());
        ClassLoaderUtil.runWithClassLoader(ClasspathResourceLoader.class.getClassLoader(), () -> ENGINE.init(p));
    }

    @Description("导出指定环境")
    public static void exportEnv(String envName, HttpConfig httpConfig, String exportPath) throws IOException {
        VelocityContext context = new VelocityContext();
        context.put("envName", envName);
        context.put("httpConfig", httpConfig);
        context.put("protocol", httpConfig.getProtocol().name().toLowerCase());
        renderEnvTemplate(context, envName, exportPath);
    }

    @Description("导出所有环境")
    public static void exportAllEnv(Map<String, HttpConfig> httpConfigMap, String exportPath) throws IOException {
        for (Map.Entry<String, HttpConfig> e : httpConfigMap.entrySet()) {
            exportEnv(e.getKey(), e.getValue(), exportPath);
        }
    }

    private static void renderEnvTemplate(VelocityContext context, String envName, String exportPath) throws IOException {
        renderTemplate(context, ENV_TEMPLATE_PATH, EXPORT_ENV_FILE_NAME, envName, exportPath);
    }

    @Description("模块导出所有 API 接口")
    public static void exportAllModuleApi(Project project, String exportPath) throws IOException {
        VelocityContext context = new VelocityContext();
        Collection<Module> moduleList = ProcessorContext.projectModules(project);

        for (Module m : moduleList) {
            // Module
            String contextPath = ProcessorContext.modulePath(m, project);
            String moduleName = m.getName();
            context.put("moduleName", moduleName);

            // Controller
            List<? extends PsiElement> controllers = ProcessorContext.moduleControllers(project, m, new ArrayList<>());
            List<String> controllerItems = new ArrayList<>();

            // Method
            Map<String, List<PostmanProcessor.PostmanItem>> methodMap = new HashMap<>();

            for (PsiElement controller : controllers) {
                String classSwagger = getClassLayerName(controller);
                List<PostmanProcessor.PostmanItem> postmanItemList = ProcessorContext.postmanControllerItems(controller, contextPath);
                if (!postmanItemList.isEmpty()) {
                    controllerItems.add(classSwagger);
                    methodMap.put(classSwagger, postmanItemList);
                }
            }

            context.put("methodMap", methodMap);
            context.put("controllerItems", controllerItems);

            renderApiTemplate(context, moduleName, exportPath);
        }
    }

    private static String getClassLayerName(PsiElement element) {
        String classSwagger = ProcessorContext.controllerSwagger(element);
        if (element instanceof KtClass kt) {
            return CharSequenceUtil.isEmpty(classSwagger) ? kt.getName() : classSwagger;
        } else if (element instanceof PsiClass c) {
            return CharSequenceUtil.isEmpty(classSwagger) ? c.getName() : classSwagger;
        }
        return "";
    }

    private static void renderApiTemplate(VelocityContext context, String moduleName, String exportPath) throws IOException {
        renderTemplate(context, API_TEMPLATE_PATH, EXPORT_API_FILE_NAME, moduleName, exportPath);
    }

    private static void renderTemplate(VelocityContext context, String templateFilePath, String templateExportFilename, String filename, String exportPath) throws IOException {
        // 加载模板
        Template template = ENGINE.getTemplate(templateFilePath, StandardCharsets.UTF_8.name());
        // 加载模板
        String fileName = CharSequenceUtil.format(templateExportFilename, filename);
        try (
                StringWriter sw = new StringWriter();
                FileWriter fw = new FileWriter(exportPath + "/" + fileName, StandardCharsets.UTF_8)
        ) {
            template.merge(context, sw);
            fw.write(sw.toString());
        }
    }
}
